
package com.joysuch.wwyt.core.controller;

import cn.zlg.common.util.MimeUtils;
import com.joysuch.wwyt.core.bean.AttachmentPreviewInfo;
import com.joysuch.wwyt.core.bean.BaseAttachmentBean;
import com.joysuch.wwyt.core.bean.dto.FileChunkUploadDTO;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.entity.BaseAttachmentRepo;
import com.joysuch.wwyt.core.enums.FileConvertStates;
import com.joysuch.wwyt.core.service.BaseAttachmentRepositoryService;
import com.joysuch.wwyt.util.FileUtil;
import com.joysuch.wwyt.workflow.api.service.NewProcessService;
import io.swagger.annotations.Api;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

@Controller
@RequestMapping("/file")
@Api(tags = "/file 文件")
public class FileUploadController {

    @Autowired
    private BaseAttachmentRepositoryService fileService;

    @Value("${wwyt.base.serverUrl}")
    private String serverUrl;

    @Value("${wwyt.file.upload.dir}")
    private String fileUploadDir;

    @Autowired
    private NewProcessService newProcessService;
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 查询话题列表
     *
     * @return
     */
    @RequestMapping("/upload")
    @ResponseBody
    public Object uploadImageForWangEditor(MultipartHttpServletRequest request) {
        try {
            Map<String, MultipartFile> fileMap = request.getFileMap();
            if (fileMap.size() > 10) {
                Assert.state(false, "一次最多上传10个文件");
            }
            List<BaseAttachmentBean> attachments = new ArrayList<>(fileMap.size());
            for (Entry<String, MultipartFile> e : fileMap.entrySet()) {
                Long id = fileService.saveFile(e.getValue(), fileUploadDir);
                BaseAttachmentBean bean = new BaseAttachmentBean();
                bean.setDownloadUrl(serverUrl + "/file/download?id=" + id);
                bean.setId(id);
                bean.setName(e.getValue().getOriginalFilename());
                attachments.add(bean);
            }
            return ResultBean.success(attachments);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResultBean.fail(103, "文件上传失败");
    }

    /**
     * 文件校验
     */
    @GetMapping("/chunk-upload")
    @ResponseBody
    public ResultBean checkFile(FileChunkUploadDTO dto) {
        return ResultBean.success(fileService.checkFile4ChunkIndex(dto));
    }

    /**
     * 文件上传
     */
    @PostMapping("/chunk-upload")
    @ResponseBody
    public ResultBean chunkUploadFile(FileChunkUploadDTO dto) {
        Long fileId = fileService.chunkUploadFile(dto);
        BaseAttachmentBean bean = new BaseAttachmentBean();
        bean.setDownloadUrl(serverUrl + "/file/download?id=" + fileId);
        bean.setId(fileId);
        bean.setName(dto.getFilename());
        return ResultBean.success(bean);
    }

    /**
     * 获取图片
     *
     * @return
     */
    @RequestMapping("download")
    public void image(Long id, HttpServletRequest request, HttpServletResponse response) {
        try {
            BaseAttachmentRepo image = fileService.findById(id);
            if (image == null) {
                response.setStatus(404);
                response.getOutputStream().flush();
            } else {
                File imageFile;
                imageFile = new File(fileUploadDir + File.separator + image.getPath());
                response.setContentType(getContentType(imageFile));
                // 1.设置文件ContentType类型，这样设置，会自动判断下载文件类型
                // 2.设置文件头：最后一个参数是设置下载文件名(假如我们叫a.pdf)
                String filename = image.getFileName();
                String agent = request.getHeader("User-Agent");
                if (agent.contains("MSIE")) {
                    // IE浏览器
                    filename = URLEncoder.encode(filename, "utf-8");
                    filename = filename.replace("+", " ");
                } else {
                    // 其它浏览器
                    filename = URLEncoder.encode(filename, "utf-8");
                }
                response.setHeader("Content-Disposition", "attachment;fileName=" + filename);
                if (imageFile.exists()) {
                    byte[] cache = new byte[2048];
                    int len = 0;
                    FileInputStream fis = new FileInputStream(imageFile);
                    while ((len = fis.read(cache)) != -1) {
                        response.getOutputStream().write(cache, 0, len);
                    }
                    fis.close();
                    response.getOutputStream().flush();
                } else {
                    response.setStatus(404);
                    response.getOutputStream().flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RequestMapping("download-v2")
    public void imageV2(HttpServletRequest request, HttpServletResponse response, String fileUrl) {
        if (StringUtils.isEmpty(fileUrl)) {
            return;
        }
        if (fileUrl.startsWith("?id")) {
            String[] items = fileUrl.split("=");
            long id = Long.parseLong(items[1]);
            image(id, request, response);
        } else if (StringUtils.isNotEmpty(fileUrl)) {
            int i = fileUrl.lastIndexOf("/");

            String fileName = fileUrl.substring(i + 1);
            String workflowUrl = newProcessService.getWorkflowUrl();
            HttpHeaders headers = newProcessService.getHeaders();
            headers.add("Content-Type", "image/jpeg");
            HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<MultiValueMap<String, Object>>(headers);
            // Resource类写出来，方便参考
            ResponseEntity<Resource> responseEntity = restTemplate.exchange(workflowUrl+"/api/file/download?fileUrl=" + fileUrl + "&fileName=" + fileName, HttpMethod.GET, formEntity, org.springframework.core.io.Resource.class);

            InputStream inputStream = null;
            try {
                // 获取响应中的body
                inputStream = responseEntity.getBody().getInputStream();
                // 转换成流
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int num = inputStream.read(buffer);
                while (num != -1) {
                    baos.write(buffer, 0, num);
                    num = inputStream.read(buffer);
                }
                baos.flush();
                byte[] data = baos.toByteArray();
                // spring boot使用ServletRequestAttributes获取request或者response等
                ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletResponse resp = requestAttributes.getResponse();
                try {
                    resp.getOutputStream().write(data);
                    resp.getOutputStream().flush();
                    resp.getOutputStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

    }

    @RequestMapping("get-preview-info")
    @ResponseBody
    public Object getPreviewInfo(Long id, HttpServletRequest request, HttpServletResponse response) {
        BaseAttachmentRepo image = fileService.findById(id);
        AttachmentPreviewInfo info = new AttachmentPreviewInfo();
        if (image == null) {
            info.setCanPreview(false);
            info.setStateName("文件不存在");
            return ResultBean.success(info);
        }

        if (Objects.isNull(image.getIntegrity()) || (Objects.nonNull(image.getIntegrity()) && image.getIntegrity()==1)) {
            info.setSupport(false);
            info.setCanPreview(false);
            info.setStateName("课件未上传完成，请在【课件管理】菜单中继续上传当前课件！");
            return ResultBean.success(info);
        }

        info.setAppVideoUrl(image.getPath());
        info.setSupport(true);
        info.setId(id);
        if (isImage(image.getType())) {
            // 图片
            info.setCanPreview(true);
            info.setFormat(image.getType());
            info.setType("image");
            info.setUrl("/file/preview?id=" + id);
        } else if ("pdf".equals(image.getType()) || "txt".equals(image.getType())) {
            // 图片
            info.setCanPreview(true);
            info.setFormat(image.getType());
            info.setType(image.getType());
            info.setUrl("/file/preview?id=" + id);
        } else if ("mp4".equals(image.getType())) {
            // 图片
            info.setCanPreview(true);
            info.setFormat(image.getType());
            info.setType("video");
            info.setUrl("/file/preview?id=" + id);
        } else if (FileConvertStates.DO_NOT_SUPPORT.is(image.getConvertState())) {
            info.setSupport(false);
            info.setCanPreview(false);
            info.setStateName("该格式文件不支持预览");
            return ResultBean.success(info);
        } else {
            info.setCanPreview(FileConvertStates.SUCCESS.is(image.getConvertState()));
            info.setStateName(FileConvertStates.findNameByCode(image.getConvertState()));
            info.setFormat(image.getConvertFormat());
            info.setType(parseType(image));
            info.setUrl("/file/preview?id=" + id);
        }
        return ResultBean.success(info);
    }


    @RequestMapping("get-new-preview-info")
    @ResponseBody
    public Object getNewPreviewInfo(Long id, HttpServletRequest request, HttpServletResponse response) {
        BaseAttachmentRepo image = fileService.findById(id);
        AttachmentPreviewInfo info = new AttachmentPreviewInfo();
        if (image == null) {
            info.setCanPreview(false);
            info.setStateName("文件不存在");
            return ResultBean.success(info);
        }
        info.setAppVideoUrl(image.getPath());
        info.setSupport(true);
        info.setId(id);
        if (isImage(image.getType())) {
            // 图片
            info.setCanPreview(true);
            info.setFormat(image.getType());
            info.setType("image");
            info.setUrl("/file/preview?id=" + id);
        } else if ("pdf".equals(image.getType()) || "txt".equals(image.getType())) {
            // 图片
            info.setCanPreview(true);
            info.setFormat(image.getType());
            info.setType(image.getType());
            info.setUrl("/file/preview?id=" + id);
        } else if ("mp4".equals(image.getType())) {
            // 图片
            info.setCanPreview(true);
            info.setFormat(image.getType());
            info.setType("video");
            info.setUrl("/file/preview?id=" + id);
        } else if (FileConvertStates.DO_NOT_SUPPORT.is(image.getConvertState())) {
            info.setSupport(false);
            info.setCanPreview(false);
            info.setStateName("该格式文件不支持预览");
            return ResultBean.success(info);
        } else {
            info.setCanPreview(FileConvertStates.SUCCESS.is(image.getConvertState()));
            info.setStateName(FileConvertStates.findNameByCode(image.getConvertState()));
            info.setFormat(image.getConvertFormat());
            info.setType(parseType(image));
            info.setUrl("/file/preview?id=" + id);
        }
        response.setHeader("Content-Length", String.valueOf(image.getFileSize()));
        byte[] bytes=new byte[image.getFileSize()];
        handleResponseDataAndHeader(request, response, bytes);
        return ResultBean.success(info);
    }

    /**
     * 处理资源响应数据和响应头
     * @param request
     * @param response
     * @param data
     * @return
     */
    private byte[] handleResponseDataAndHeader(HttpServletRequest request, HttpServletResponse response, byte[] data) {


        String rangeString = request.getHeader("Range");
        if (StringUtils.isEmpty(rangeString)) {
            return data;
        }
        return handleExistRangeResponseDataAndHeader(request, response, data);
    }

    /**
     * 处理请求头带range的资源响应数据和响应头
     * @param request
     * @param response
     * @param data
     * @return
     */
    private byte[] handleExistRangeResponseDataAndHeader(HttpServletRequest request,
                                                         HttpServletResponse response, byte[] data) {
        // 一次返回2M数据
        int onceDataLength = 2 * 1024 * 1024;
        String rangeString = request.getHeader("Range");
        int[] rangeArray = getRangeArrayFromRangeString(rangeString, onceDataLength);
        if (rangeArray[1] > data.length-1) {
            rangeArray[1] = data.length-1;
        }
        response.setHeader("Content-Range",
                "bytes " + rangeArray[0] + "-" + rangeArray[1] + "/" + data.length);
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        response.addHeader("Accept-Ranges", "bytes");
        int byteLength = rangeArray[1] - rangeArray[0] + 1;
        response.setContentLength(byteLength);
        byte[] onceData = new byte[byteLength];
        System.arraycopy(data, rangeArray[0], onceData, 0, byteLength);
        return onceData;
    }

    /**
     * 获取请求需要的数据范围
     * @param rangeString
     * @param dataLength
     * @return
     */
    private int[] getRangeArrayFromRangeString(String rangeString, int dataLength) {
        int[] rangeArray = new int[2];
        rangeString = rangeString.substring(rangeString.indexOf("=") + 1);
        String rangeStartString = rangeString.substring(0, rangeString.indexOf("-"));
        if (StringUtils.isNotBlank(rangeStartString.trim())) {
            rangeArray[0] = Integer.parseInt(rangeStartString.trim());
        } else {
            rangeArray[0] = 0;
        }

        if (rangeString.indexOf("-") == rangeString.length()-1) {
            rangeArray[1] = rangeArray[0] + dataLength - 1;
        } else {
            String rangeEndString = rangeString.substring(rangeString.indexOf("-") + 1);
            if (StringUtils.isNotBlank(rangeEndString.trim())) {
                rangeArray[1] = Integer.parseInt(rangeEndString.trim());
            } else {
                rangeArray[1] = rangeArray[0] + dataLength - 1;
            }
        }
        return rangeArray;
    }

    private String parseType(BaseAttachmentRepo image) {
        String format = image.getConvertFormat();
        if ("pdf".equals(format)) {
            return "pdf";
        } else if ("mp4".equals(format)) {
            return "video";
        } else if (isImage(image.getType())) {
            // 图片不转换
            return "image";
        }
        return "unknown";
    }

    private boolean isImage(String suffix) {
        return "jpg".equals(suffix) || "jpeg".equals(suffix) || "png".equals(suffix) || "gif".equals(suffix)
                || "bmp".equals(suffix);
    }

    /**
     * 获取图片
     *
     * @return
     */
    @RequestMapping("preview")
    public void preview(Long id, HttpServletRequest request, HttpServletResponse response) {
        try {
            BaseAttachmentRepo image = fileService.findById(id);
            System.out.println(image);
            if (image == null) {
                response.setStatus(404);
                response.getOutputStream().flush();
            } else {
                File imageFile;
                imageFile = getDownloadFilePath(image);
                System.out.println(imageFile.getAbsolutePath());
                response.setContentType(getContentType(imageFile));
                if (response.getContentType().contains("text")) {
                    response.setCharacterEncoding(parseEncodingFromFile(imageFile));
                    System.out.println("file encoding is " + response.getCharacterEncoding());
                }
                System.out.println(response.getContentType());
                // 1.设置文件ContentType类型，这样设置，会自动判断下载文件类型
                // 2.设置文件头：最后一个参数是设置下载文件名(假如我们叫a.pdf)
                String filename = image.getFileName();
                String agent = request.getHeader("User-Agent");
                if (agent.contains("MSIE")) {
                    // IE浏览器
                    filename = URLEncoder.encode(filename, "utf-8");
                    filename = filename.replace("+", " ");
                } else {
                    // 其它浏览器
                    filename = URLEncoder.encode(filename, "utf-8");
                }
                // response.setHeader("Content-Disposition", "attachment;fileName=" + filename);
                if (imageFile.exists()) {
                    byte[] cache = new byte[2048];
                    int len = 0;
                    FileInputStream fis = new FileInputStream(imageFile);
                    while ((len = fis.read(cache)) != -1) {
                        response.getOutputStream().write(cache, 0, len);
                    }
                    fis.close();
                    response.getOutputStream().flush();
                } else {
                    response.setStatus(404);
                    response.getOutputStream().flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String parseEncodingFromFile(File imageFile) {
        return FileUtil.getFileEncoding(imageFile, "UTF-8");
    }

    private File getDownloadFilePath(BaseAttachmentRepo image) {
        if (isImage(image.getType()) || "txt".equals(image.getType()) || "pdf".equals(image.getType())
                || "mp4".equals(image.getType())) {
            // 原路径
            return new File(fileUploadDir + File.separator + image.getPath());
        }
        // 返回转换后的文件路径
        return new File(fileUploadDir + File.separator + image.getConvertPath());
    }

    private String getContentType(File imageFile) {
        return MimeUtils.getMimeTypeWithDefault(FilenameUtils.getExtension(imageFile.getName()));
    }

}
